package com.franklin.java.linemarker.sql;

import com.franklin.java.api.annotation.BaseAnnotation;
import com.franklin.java.api.linemarker.AbstractLineMarkerProvider;
import com.franklin.java.api.mybatis.annotation.MyBatisAnnotation;
import com.franklin.java.api.mybatis.annotation.MyBatisAnnotations;
import com.franklin.java.api.mybatis.dom.utils.MapperUtils;
import com.franklin.java.icon.PluginIcons;
import com.google.common.collect.ImmutableSet;
import com.intellij.codeInsight.daemon.LineMarkerInfo;
import com.intellij.pom.Navigatable;
import com.intellij.psi.PsiAnnotation;
import com.intellij.psi.PsiClass;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiMethod;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.psi.xml.XmlTag;
import org.jetbrains.annotations.NotNull;

import javax.swing.*;
import java.util.Collections;
import java.util.Objects;
import java.util.Optional;


/**
 * @author Franklin
 * @since 2021/9/6 15:53
 */
public class SelectSqlLineMarkerProvider extends AbstractLineMarkerProvider<PsiElement> {

    public SelectSqlLineMarkerProvider() {
        BaseAnnotation.initialize();
    }

    private static final ImmutableSet<String> TARGET_TAG_NAMES = ImmutableSet.of(
        "select",
        "sql"
    );

    private boolean isTargetTag(PsiElement psiElement) {
        XmlTag xmlTag = (XmlTag) psiElement;
        return TARGET_TAG_NAMES.contains(xmlTag.getName());
    }

    protected boolean isXmlTarget(@NotNull PsiElement psiElement) {
        return psiElement instanceof XmlTag
            && MapperUtils.isElementWithinMybatisFile(psiElement)
            && isTargetTag(psiElement);
    }

    @Override
    protected boolean isMarkerTarget(@NotNull PsiElement psiElement) {
        return isMapperTarget(psiElement) || isXmlTarget(psiElement);
    }

    private boolean isMapperTarget(@NotNull PsiElement psiElement) {
        if (psiElement instanceof PsiMethod) {
            PsiMethod psiMethod = (PsiMethod) psiElement;
            MyBatisAnnotations.Select select = MyBatisAnnotation.get(MyBatisAnnotations.Select.class);
            PsiAnnotation psiAnnotation = Optional.ofNullable(PsiTreeUtil.getParentOfType(psiMethod, PsiClass.class))
                .filter(PsiClass::isInterface)
                .map(psiClass -> psiMethod.getAnnotation(select.getQualifiedName()))
                .orElse(null);
            return Objects.nonNull(psiAnnotation);
        }
        return false;
    }

    @Override
    public Icon getIcon() {
        return PluginIcons.SERVICE;
    }

    @Override
    public LineMarkerInfo<PsiElement> getLineMarkerInfo(@NotNull PsiElement psiElement) {
        if (!isMarkerTarget(psiElement)){
            return null;
        }

        return new SelectSqlLineMarkerInfo(
            psiElement,
            getIcon(),
            (Navigatable) psiElement.getNavigationElement(),
            Collections.singleton("")
        );
    }

}
